#include "def.h"
#include "gui.h"
#include "plugin.h"
#include "sync.h"
#include "net.h"
#include "util.h"
#include "main.h"	//For mfnplugin
#include <stdio.h>

#pragma message ("remove when VTRY/VCATCH refactored")
#include "..\Global\globaldef.h"
#include "..\CommLib\PacketConven.h"

#include <maya/MGlobal.h>
#include <maya/MFnDagNode.h>
#include <maya/MCallbackIdArray.h>
#include <maya/MFnMesh.h>

int ChannelStrToInt(MString& name) {

	if(name == "color")
		return 1;
	if(name == "normal")
		return 2;
	if(name == "aux")
		return 3;

	return -1;
}

void VGUI::UserInteractionHandler::SetHandler(const MArgList& args) {

	MString functionID = args.asString(0);
	
	if(args.asString(1) == "mesh") {

		MObject targetMesh;
		if(!VUtil::GetMeshByName(args.asString(2), targetMesh))
			return;

		/***************************************************************************************************
		Mesh geoemtry part changes
		****************************************************************************************************/
		if(args.asString(3) == "part") {

			MString partIndex = args.asString(4);
			MString partTarget = args.asString(5);
			MString targetValue = args.asString(6);
			MString partAttribute = MString("lv_part_" + partIndex);

			if(partTarget == "shader") {

				if(targetValue.length() > 0) {

					VUtil::SetStringArrayAttribute(targetMesh, partAttribute, 0, targetValue);
					VNet::SendPart_Shader(targetMesh, partIndex.asInt(), targetValue, false);
				}
				else {
				
					VNet::SendPart_Shader(targetMesh, partIndex.asInt(), targetValue, true);
				}
			}
			else 
			{
				MString channel = partTarget;

				if(targetValue == MString("add")) {

					VNet::SendPart_ChannelChange(targetMesh, partIndex.asInt(), partTarget, false);
				}
				else
				if(targetValue == MString("remove")) {

					VNet::SendPart_ChannelChange(targetMesh, partIndex.asInt(), partTarget, true);
				}
				else
				if(targetValue == MString("add_sampler")) {

					MObject sampler;
					if(VUtil::GetSamplerByName(args.asString(7), sampler))
						VNet::SendPart_SamplerChange(targetMesh, sampler, partIndex.asInt(), channel, false);
				}
				else
				if(targetValue == MString("remove_sampler")) {

					MObject sampler;
					if(VUtil::GetSamplerByName(args.asString(7), sampler))
						VNet::SendPart_SamplerChange(targetMesh, sampler, partIndex.asInt(), channel, false);
				}

				//if(partTarget == "channel") {
				//	MString value = args.asString(7);
				//	bool state = (value == "remove");
				//	VNet::SendPart_ChannelChange(targetMesh, partIndex.asInt(), targetValue, state);
				//	
				//	/*
				//	Currently, only other mesasge targets are for samplers
				//	*/
				//	/*MObject sampler;
				//	MStringArray tokens;
				//	targetValue.split('_', tokens);
				//	if(tokens.length() > 0 && !VUtil::GetSamplerByName(tokens[0], sampler)) {

				//		VUtil::SetStringArrayAttribute(targetMesh, partAttribute, 1, targetValue);
				//		VNet::SendPart_ChannelChange(targetMesh, partIndex.asInt(), partTarget, false);
				//	}
				//	else {
				//	
				//		VNet::SendPart_ChannelChange(targetMesh, partIndex.asInt(), partTarget, true);
				//	}*/
				//				
				//	VUtil::SetStringArrayAttribute(targetMesh, partAttribute, 1, targetValue);
				//}
			}
		}
		/***************************************************************************************************
		Mesh general properties
		****************************************************************************************************/
		else 
		if(args.asString(3) == "vertex_format") {

			int vertexFormat = args.asInt(4);
			VUtil::SetAttribute(targetMesh, MString("lv_format"), vertexFormat);
			VNet::SendMesh_Geometry(targetMesh, false);
		}
		else 
		if(args.asString(3) == "intancable") {

			bool isInstanced = args.asInt(4) == 1;
			VNet::SendMesh_Parameters(targetMesh, isInstanced, UNCHANGED, -1.0f);
		}
		else 
		if(args.asString(3) == "deformable") {

			bool isDeformable = args.asInt(4) == 1;
			VNet::SendMesh_Parameters(targetMesh, UNCHANGED, isDeformable, -1.0f);
		}
		/***************************************************************************************************
		Mesh instancing changes
		****************************************************************************************************/
		else 
		if(args.asString(3) == "instancing") {

			MString childName = args.asString(4);
			MString childAction = args.asString(5);
			MString targetValue = args.asString(6);
			MString childAttribute = MString("lv_instchild_") + childName;
			
			MObject childObject;
			MObject samplerObject;
			VUtil::GetMeshByName(childName, childObject);

			if(childAction == "add") {

				VNet::SendInstancing_ChildChange(targetMesh, childObject, false);
			}
			else
			if(childAction == "remove") {

				VNet::SendInstancing_ChildChange(targetMesh, childObject, true);
			}
			else
			if(childAction == "channel_uv") {

				VNet::SendInstancing_ChildUV(targetMesh, childObject, targetValue.asInt());
			}
			else
			if(childAction == "sampler_mask") {

				if(VUtil::GetSamplerByName(targetValue, samplerObject))
					VNet::SendInstancing_ChildSampler(targetMesh, childObject, samplerObject, MString("mask"));
			}
			else
			if(childAction == "sampler_rotate") {

				if(VUtil::GetSamplerByName(targetValue, samplerObject))
					VNet::SendInstancing_ChildSampler(targetMesh, childObject, samplerObject, MString("rotate"));
			}
			else
			if(childAction == "sampler_scale") {

				if(VUtil::GetSamplerByName(targetValue, samplerObject))
					VNet::SendInstancing_ChildSampler(targetMesh, childObject, samplerObject, MString("scale"));
			}
			else
			if(childAction == "filename_mask") {

				VNet::SendInstancing_ChildMask(targetMesh, childObject, targetValue);
			}
		}
		/***************************************************************************************************
		Mesh deformation changes
		****************************************************************************************************/
		else 
		if(args.asString(3) == "deformation") {

			MString deformAction = args.asString(4);

			if(deformAction == "scale") {

				float scale = args.asDouble(5);
				VNet::SendMesh_Parameters(targetMesh, UNCHANGED, UNCHANGED, scale);
			}
		}

		VGUI::Refresh::SelectionParameters(targetMesh);
	}
	else
	if(args.asString(1) == "scene") {

		if(args.asString(2) == "create") {
			
			if(args.asString(3) == "sampler") {

				/*VNet::SendScene_SamplerChange(*/
			}
		}
		else
		if(args.asString(2) == "remove") {
			
			if(args.asString(3) == "sampler") {

			}
		}
	}
}

void VGUI::UserInteractionHandler::DoHandler(const MArgList& args) {

	uint argCount = args.length();
	if(argCount <= 1)
		return;

	if(args.asString(1) == "connect") {

		MString host = args.asString(2);

		if(VNet::IsConnected()) {

			if(VNet::Disconnect())
				VNet::Connect();
		} 
		else {

			VNet::Connect();
			VSyncViewer::EntireScene();
		}

		VGUI::Refresh::ViewServer();
	}
	else
	if(args.asString(1) == "connect_autosync") {

		bool autosync = args.asInt(2) == 1;
	}
	else
	if(args.asString(1) == "vertex_format") {

		int vertexFormat = args.asInt(2);
		MIntArray vertexFormatArray;

		vertexFormatArray.append(vertexFormat & VERTEX_POSITION ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_NORMAL ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_TANGENT ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_UV0 ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_UV1 ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_UV2 ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_COLOR ? 1 : 0);
		vertexFormatArray.append(vertexFormat & VERTEX_DEFORMATION ? 1 : 0);
		setResult(vertexFormatArray);
	}
}

void VGUI::UserInteractionHandler::MessageHandler(const MArgList& args) {

	uint argCount = args.length();
	if(argCount <= 1)
		return;
	
	if(args.asString(0) == "set")
		VGUI::UserInteractionHandler::SetHandler(args);
	else
	if(args.asString(0) == "do")
		VGUI::UserInteractionHandler::DoHandler(args);

	MString functionID = args.asString(0);
	MObject targetObject;
}

bool VGUI::Initialize() {

	VTRY {
			
		MObject pluginObject;
		if(!VPlugin::GetPlugin(pluginObject))
			VTHROW_INTERNALERROR("Failed to get plugin object.");

		MFnPlugin plugin(pluginObject);

		//Forces the mel gui script to reload
		//Useful for debugging/development
		if(MAYA_FAIL(MGlobal::executeCommand("source lvUtilities.mel; source lvEvents.mel; source lvInit.mel", false, false)))
			VTHROW_INTERNALERROR("Failed to update GUI create script.");

		if(MAYA_FAIL(MGlobal::executeCommand("source lvShutdown.mel", false, false)))
			VTHROW_INTERNALERROR("Failed to update GUI release script.");

		if(MAYA_FAIL(plugin.registerUI("lvInit", "lvShutdown")))
			VTHROW_INTERNALERROR("Failed to register GUI");

		if(MAYA_FAIL(plugin.registerCommand( "CallFunction", VGUI::UserInteractionHandler::Instantiate )))
			VTHROW_INTERNALERROR("Failed to register GUI command");

		return true;
	}
	VCATCH;

	VGUI::Release();
	return false;
}

bool VGUI::Release() {

	VTRY {

		MObject pluginObject;
		if(!VPlugin::GetPlugin(pluginObject))
			VTHROW_INTERNALERROR("Failed to get plugin object.");

		MFnPlugin plugin(pluginObject);
		if(MAYA_FAIL(plugin.deregisterCommand("CallFunction")))
			VTHROW_INTERNALERROR("Failed to deregister interface command.");
	
		return true;
	}
	VCATCH;

	return false;
}

bool VGUI::Refresh::Interface(MObject& target) {

	bool result = true;

	result &= VGUI::Refresh::ViewServer();
	result &= VGUI::Refresh::SynchronizeSelection(target);
	result &= VGUI::Refresh::SelectionParameters(target);
	result &= VGUI::Refresh::ProceduralPopulation(target);
	result &= VGUI::Refresh::DeformationParameters(target);

	return result;
}

/**
	Updates components on the deformation panel
	
	@author Dacre Denny
	@return true if successful, false otherwise
*/
bool VGUI::Refresh::DeformationParameters(MObject& target) {
	
	MString parameter;

	if(!VUtil::IsMesh(target)) {
	
		parameter = "";
	}
	else {

		if(!VUtil::GetObjectName(target, parameter))
			return false;
	}

	MString commandString = "UpdateDeformationPanel(" + MAYA_STRINGSTRING( parameter ) + ");";
	MAYA_RETURN_IF_FAIL(MGlobal::executeCommand(commandString, false, false), false);
	return true;

}

/**
	Updates components on the connection panel
	
	@author Dacre Denny
	@return true if successful, false otherwise
*/
bool VGUI::Refresh::ViewServer() {
	
	MString command = "UpdateConnectionPanel(\"" + VNet::Status() + "\"," + (VNet::IsConnected() ? "1" : "0") + ");";
	MAYA_RETURN_IF_FAIL(MGlobal::executeCommand(command, false, false), false);

	return true;
}

/**
	Update the parameters panel based on the current selection
	
	@author Dacre Denny
	@return true if successful, false otherwise
*/
bool VGUI::Refresh::SelectionParameters(MObject& target) {

	MString parameter;

	if(!VUtil::IsMesh(target)) {
	
		parameter = "";
	}
	else {

		if(!VUtil::GetObjectName(target, parameter))
			return false;
	}
		
	MString command = ("UpdateSelectionParametersPanel(\"" + parameter + "\");");
	MAYA_RETURN_IF_FAIL(MGlobal::executeCommand(command, false, false), false);
	return true;
}

/**
	Update the current select information panel
	
	@author Dacre Denny
	@return true if successful, false otherwise
*/
bool VGUI::Refresh::SynchronizeSelection(MObject& target) {
	
	MString parameter;
	if(!VUtil::IsMesh(target)) {
	
		parameter = "";
	}
	else {

		if(!VUtil::GetObjectName(target, parameter))
			return false;
	}

	MString command = ("UpdateCurrentSelectionPanel(\"" + parameter + "\");");
	MAYA_RETURN_IF_FAIL(MGlobal::executeCommand(command, false, false), false);
	return true;
}

bool VGUI::Refresh::ProceduralPopulation(MObject& target) {
	
	MString parameterString;

	if(VUtil::IsMesh(target)) {

		MStatus result;
		MFnDagNode meshDN(target, &result);
		MAYA_RETURN_IF_FAIL(result, false);
		
		parameterString = meshDN.name();
	}
	else {

		parameterString = "";
	}

	MString commandString = "UpdateInstancingPanel(" + MAYA_STRINGSTRING( parameterString ) + ");";
	MAYA_RETURN_IF_FAIL(MGlobal::executeCommand(commandString, false, false), false);
	return true;
}